#include <odb/database.hxx>
#include <odb/mysql/database.hxx>
#include "student.hxx"
#include "student-odb.hxx"
#include <gflags/gflags.h> 

DEFINE_string(host, "127.0.0.1", "这是Mysql服务器地址");
DEFINE_int32(port, 3306, "这是Mysql服务器端口");
DEFINE_string(db, "TestDB", "数据库默认库名称");
DEFINE_string(user, "root", "这是Mysql用户名");
DEFINE_string(pswd, "2322wf176210", "这是Mysql密码");
DEFINE_string(cset, "utf8", "这是Mysql客户端字符集");
DEFINE_int32(max_pool, 3, "这是Mysql连接池最大连接数量");

void insert_classes(odb::mysql::database& db)   //对insert进行封装, 让操作更简单
{
    try{    //操作的时候, 可能抛异常, 就需要捕捉异常, 否则会程序崩溃
        // 获取事务对象, 开启事务
        odb::transaction trans(db.begin());
        Classes c1("一年级一班");   // 实例化出对象, 表里的内容就相当于对象, 表结构就相当于是类
        Classes c2("一年级二班");
        db.persist(c1);
        db.persist(c2);
        // 提交事务
        trans.commit();
    }catch (std::exception& e) {
        std::cout << "插入数据失败: " << e.what() << std::endl;
    }
}

void insert_student(odb::mysql::database& db)
{
    try {
        // 获取事务对象开启事务
        odb::transaction trans(db.begin());
        Student s1(1, "张三", 18, 1);
        Student s2(2, "李四", 19, 1);
        Student s3(3, "王五", 18, 1);
        Student s4(4, "赵六", 15, 2);
        Student s5(5, "刘七", 18, 2);
        Student s6(6, "孙八", 23, 2);
        db.persist(s1);
        db.persist(s2);
        db.persist(s3);
        db.persist(s4);
        db.persist(s5);
        db.persist(s6);

        // 提交事务
        trans.commit();
    }catch (std::exception& e) {
        std::cout << "插入学生数据出错: " << e.what() << std::endl;
    }
}

// 直接更新会出错, 我们需要先进行查询
// void update_student(odb::mysql::database& db) {
//     try{
//         // 获取事务对象开启事务
//         odb::transaction trans(db.begin());
//         Student s1(1, "张三", 14, 1);
//         db.update(s1);  
//         // 提交事务
//         trans.commit();
//     } catch (std::exception& e) {
//         std::cout << "更新学生数据出错: " << e.what() << std::endl;
//     }
// }


void update_student(odb::mysql::database& db, Student& stu) {
    try{
        // 获取事务对象开启事务
        odb::transaction trans(db.begin());
        stu.age(15);
        db.update(stu);
        // 提交事务
        trans.commit();
    } catch (std::exception& e) {
        std::cout << "更新学生数据出错: " << e.what() << std::endl;
    }
}

Student select_student(odb::mysql::database& db)
{
    Student res;
    try {
        // 获取事务对象开启事务
        odb::transaction trans(db.begin());
        typedef odb::query<Student> query;      //这个类是过滤条件类
        typedef odb::result<Student> result;    //用于存储查询结果的容器类
        result r(db.query<Student>(query::name == "张三")); //db.query<Student> 说明是要对Student进行查询
        res = *r.begin();   //返回该容器的迭代器
        // 提交事务
        trans.commit();
    } catch (std::exception& e) {
        std::cout << "更新学生数据出错: " << e.what() << std::endl;
    }
    return res;
}

void remove_student(odb::mysql::database& db)
{
    try {
        // 获取事务对象开启事务
        odb::transaction trans(db.begin());
        typedef odb::query<Student> query;
        db.erase_query<Student>(query::name == "孙八");
        // 提交事务
        trans.commit();
    } catch (std::exception& e) {
        std::cout << "更新学生数据出错: " << e.what() << std::endl;
    }
}

// 对视图进行查询
void classes_student(odb::mysql::database& db)
{
    try {
        // 获取事务对象开启事务
        odb::transaction trans(db.begin());
        typedef odb::query<struct classes_student> query;      //这个类是过滤条件类  struct告诉编译器这是个类型
        typedef odb::result<struct classes_student> result;    //用于存储查询结果的容器类
        result r(db.query<struct classes_student>(query::classes::name == "一年级一班"));//db.query<classes_student> 说明是要对classes_student进行查询
        for (auto it = r.begin(); it != r.end(); ++it) {
            std::cout << it->_id << std::endl;
            std::cout << it->_sn << std::endl;
            std::cout << it->_name << std::endl;
            std::cout << *it->_age << std::endl;
            std::cout << it->_classes_name << std::endl;
        }
        trans.commit();
    } catch (std::exception& e) {
        std::cout << "查询视图数据出错: " << e.what() << std::endl;
    }
}

// 全量查询
void all_student(odb::mysql::database& db)
{
    try {
        // 获取事务对象开启事务
        odb::transaction trans(db.begin());
        typedef odb::query<struct all_name> query;      //这个类是过滤条件类  struct告诉编译器这是个类型
        typedef odb::result<struct all_name> result;    //用于存储查询结果的容器类
        result r(db.query<struct all_name>(""));//db.query<all_name> 说明是要对all_name进行查询, ""参数则是全量查询
        for (auto it = r.begin(); it != r.end(); ++it) {
            std::cout << it->_name << std::endl;
        }
        // 提交事务
        trans.commit();
    } catch (std::exception& e) {
        std::cout << "查询所有学生姓名数据出错: " << e.what() << std::endl;
    }
}

int main(int argc, char* argv[])
{
    google::ParseCommandLineNonHelpFlags(&argc, &argv, true);

    // 以下操作: 只需遇到, 粘贴即可, 不需要理解底层
    // 1. 构造连接池工厂配置对象    这里只能用unique_ptr, 其他的智能指针如shared_ptr会报错
    //mysql连接池对象 connection_pool_factory(std::size_t max_connections = 0, std::size_t min_connections = 0, bool ping = true); 
    std::unique_ptr<odb::mysql::connection_pool_factory> cpf(new odb::mysql::connection_pool_factory(FLAGS_max_pool, 0));
    // 2. 构造数据库操作对象
    odb::mysql::database db(FLAGS_user, FLAGS_pswd, FLAGS_db, FLAGS_host, FLAGS_port, "", FLAGS_cset, 0, std::move(cpf));
    // 数据操作
    // insert_classes(db);
    // insert_student(db);
    // auto stu = select_student(db);
    // update_student(db, stu);
    // remove_student(db);
    // classes_student(db);
    all_student(db);
    return 0;
}